package main

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	sc "github.com/hyperledger/fabric/protos/peer"
)

type SmartContract struct {
}

type Manufacturer struct {
	ManufacturerId string `json:"manufacturerId"`
	Name string `json:"Name"`
	ManufacturerOwner string `json:"manufacturerOwner"`
	Address string `json:"address"`
	Soil string `json:"soil"`
	Temperature string `json:"temperature"`
}

type ManufacturerDaily struct {
	ManufacturerDailyId string `json:"manufacturerDailyId"`
	TreeId   string `json:"treeId"`
	Fertilizer   string `json:"fertilizer"`
	Pesticide  string `json:"pesticide"`
	Watering string `json:"watering"`
	FruitSize  string `json:"fruitSize"`
	FruitSizeUnit  string `json:"fruitSizeUnit"`
}

type ManufacturerDeal struct {
	ManufacturerDealId string `json:"manufacturerDealId"`
	TreesId   string `json:"treesId"`
	PickingTreatment   string `json:"pickingTreatment"`
	TotalAmount  string `json:"totalAmount"`
	DealersId string `json:"dealersId"`
	Price  string `json:"price"`
	ManufacturerDealUnit  string `json:"manufacturerDealUnit"`
}

type Tree struct{
	TreeId string `json:"treeId"`
	Name string `json:"name"`
	Area string `json:"area"`
}

func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
	return shim.Success(nil)
}

func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {

	function, args := APIstub.GetFunctionAndParameters()
	if function == "query" {
		return s.query(APIstub, args)
	}else if function == "create" {
		return s.create(APIstub, args)
	} else if function == "change" {
		return s.change(APIstub, args)
	} else if function == "delete" {
		return s.delete(APIstub, args)
	} else if function == "queryAll" {
		return s.queryAll(APIstub, args)
	} else if function == "initLedger" {
		return s.initLedger(APIstub)
	} 

	return shim.Error("Invalid Smart Contract function name.")
}

func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
	Manufacturers := []Manufacturer{
		Manufacturer{ManufacturerId: "MANUFACTURER1", Name: "PriusA", ManufacturerOwner: "blue", Address: "UI", Soil: "IO", Temperature: "OP"},
		Manufacturer{ManufacturerId: "MANUFACTURER2", Name: "PriusB", ManufacturerOwner: "blue", Address: "UI", Soil: "IO", Temperature: "OP"},
	}

	i := 0
	for i < len(Manufacturers) {
		fmt.Println("i is ", Manufacturers[i])
		bytes, _ := json.Marshal(Manufacturers[i])
		fmt.Println(bytes)
		APIstub.PutState(Manufacturers[i].ManufacturerId, bytes)
		fmt.Println("Added", Manufacturers[i])
		i = i + 1
	}

	return shim.Success(nil)
}

func (s *SmartContract) query(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	bytes, _ := APIstub.GetState(args[0])
	fmt.Println(bytes)
	return shim.Success(bytes)
}

func (s *SmartContract) create(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	model := args[0]
	arguments := args[1:len(args)]
	switch model {
		case "manufacturer":
			s.createManufacturer(APIstub, arguments)
		case "manufacturerDaily":
			s.createManufacturerDaily(APIstub, arguments)
		case "manufacturerDeal":
			s.createManufacturerDeal(APIstub, arguments)
		case "tree":
			s.createTree(APIstub, arguments)
	}
	return shim.Success(nil)
}

func (s *SmartContract) createManufacturer(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 6 {
		return shim.Error("Incorrect number of arguments. Expecting 6")
	}
	var manufacturer = Manufacturer{
		ManufacturerId: args[0],
		Name : args[1],
		ManufacturerOwner : args[2],
		Address : args[3],
		Soil : args[4],
		Temperature : args[5]}
	bytes, _ := json.Marshal(manufacturer)
	APIstub.PutState(args[0], bytes)

	return shim.Success(nil)
}

func (s *SmartContract) createManufacturerDaily(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 7 {
		return shim.Error("Incorrect number of arguments. Expecting 7")
	}
	var manufacturerDaily = ManufacturerDaily {
		ManufacturerDailyId : args[0],
		TreeId : args[1],
		Fertilizer : args[2],
		Pesticide : args[3],
		Watering : args[4],
		FruitSize : args[5],
		FruitSizeUnit : args[6]}
	bytes, _ := json.Marshal(manufacturerDaily)
	APIstub.PutState(args[0], bytes)
	return shim.Success(nil)
}

func (s *SmartContract) createManufacturerDeal(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 7 {
		return shim.Error("Incorrect number of arguments. Expecting 7")
	}
	var manufacturerDeal = ManufacturerDeal{
		ManufacturerDealId : args[0],
		TreesId : args[1],
		PickingTreatment : args[2],
		TotalAmount : args[3],
		DealersId : args[4],
		Price : args[5],
		ManufacturerDealUnit : args[6]}
	bytes, _ := json.Marshal(manufacturerDeal)
	APIstub.PutState(args[0], bytes)
	return shim.Success(nil)
}

func (s *SmartContract) createTree(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}
	var tree = Tree {
		TreeId : args[0],
		Name : args[1],
		Area : args[2]}
	bytes, _ := json.Marshal(tree)
	APIstub.PutState(args[0], bytes)
	return shim.Success(nil)
}


func (t *SmartContract) delete(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	APIstub.DelState(args[0])
	return shim.Success(nil)
}

func (s *SmartContract) change(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	arguments := args[1:len(args)]
	s.delete(APIstub, arguments)
	s.create(APIstub, args)
	return shim.Success(nil)
}

func (s *SmartContract) queryAll(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
	model := args[0]
	var startKey string
	var endKey string
	if (model == "manufacturer") {
		startKey = "MANUFACTURER1"
		endKey = "MANUFACTURER99999999999"
	} else if (model == "manufacturerDaily") {
		startKey = "MANUFACTURERDAILY0"
		endKey = "MANUFACTURERDAILY999999999999"
	} else if (model == "manufacturerDeal") {
		startKey = "MANUFACTURERDEAL0"
		endKey = "MANUFACTURERDEAL999999999999"
	} else if (model == "tree") {
		startKey = "TREE0"
		endKey = "TREE999999999999"
	}

	resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer resultsIterator.Close()

	// buffer is a JSON array containing QueryResults
	var buffer bytes.Buffer
	buffer.WriteString("[")

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString("{\"Key\":")
		buffer.WriteString("\"")
		buffer.WriteString(queryResponse.Key)
		buffer.WriteString("\"")

		buffer.WriteString(", \"Record\":")
		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		buffer.WriteString("}")
		bArrayMemberAlreadyWritten = true
	}
	buffer.WriteString("]")

	fmt.Printf("- queryAllSellers:\n%s\n", buffer.String())

	return shim.Success(buffer.Bytes())
}

func main() {

	err := shim.Start(new(SmartContract))
	if err != nil {
		fmt.Printf("Error creating new Smart Contract: %s", err)
	}
}
